package lrc2video.zlm.tool.lv.main;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import lombok.extern.log4j.Log4j2;
import lrc2video.happy.lyrics.LyricsFileReader;
import lrc2video.happy.lyrics.model.LyricsInfo;
import lrc2video.happy.lyrics.model.LyricsLineInfo;
import lrc2video.happy.lyrics.model.LyricsTag;
import lrc2video.happy.lyrics.utils.ImageCallable;
import lrc2video.happy.lyrics.utils.LyricsIOUtils;
import lrc2video.happy.lyrics.utils.LyricsUtils;
import lrc2video.zlm.tool.lv.common.Constants;
import lrc2video.zlm.tool.lv.util.DataUtil;
import lrc2video.zlm.tool.lv.util.FontsUtil;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.mp3.MP3AudioHeader;
import org.jaudiotagger.audio.mp3.MP3File;
import top.xzxsrq.common.utils.FileUtilsZX;
import top.xzxsrq.ffmpeg.FFmpegUtils;
import top.xzxsrq.utils.PathUtils;

import java.awt.*;
import java.io.*;
import java.util.List;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

@Log4j2
public class Lrc2videoApp {

    private static final int THREADS_NUMBER = FFmpegUtils.THREADS_NUMBER;

    // 开启线程池
    public static final ExecutorService executorService = Executors.newFixedThreadPool(THREADS_NUMBER);

    public static void main(String[] args) throws Exception {
        DataUtil.init();
        Lrc2videoApp.init();
        // 关闭线程池
        executorService.shutdown();
    }


    public static void init() throws Exception {
        int viewWidth = 1920 / 2;// 图片宽度
        int viewHeight = 1080 / 2;// 图片
        int paddingHeight = 10;// 歌词间隔
        int fontSize = 60;
        String path = "C:\\Users\\27451\\Desktop\\音乐";
        if (FileUtil.isDirectory(path)) {
            runDirFile(viewWidth, viewHeight, paddingHeight, fontSize, path);
        } else {
            runSingFile(viewWidth, viewHeight, paddingHeight, fontSize, path);
        }
    }

    /**
     * 处理当个文件
     *
     * @param viewWidth     图片宽度
     * @param viewHeight    图片高度
     * @param paddingHeight 歌词间隔
     * @param fontSize      歌词文件小打
     * @param lrcPath       lrc文件路径
     * @throws Exception
     */
    public static void runSingFile(int viewWidth, int viewHeight, int paddingHeight, int fontSize, File lrcPath) throws Exception {
        log.info("开始处理的歌词文件 = " + lrcPath.getAbsolutePath());
        // 判断是否存在ass文件，存在使用ass文件
        String assFilePath = ImageCallable.getAssFilePath(lrcPath);
        File file1 = new File(assFilePath);
        if (file1.exists()) {
            lrcPath = file1;
        }
        String audioFilePath = ImageCallable.getAudioFilePath(lrcPath);
        log.info("对应的mp3文件地址：{}", audioFilePath);
        if (!FileUtilsZX.exists(new File(audioFilePath))) {
            log.error("对应的mp3文件不存在");
            return;
        }
        String outFilePath = ImageCallable.getOutFilePath(lrcPath);
        MP3File mp3File = (MP3File) AudioFileIO.read(new File(audioFilePath));
        MP3AudioHeader audioHeader = (MP3AudioHeader) mp3File.getAudioHeader();
        double mp3TimeLength = audioHeader.getPreciseTrackLength();//单位为秒
        // 视频和音频长度相差2秒认为是相等的
        double count = 2;
        // 如果有对应的输出文件就跳过
        if (FileUtilsZX.exists(new File(outFilePath))) {
            // 判断mp3和mp4时间的长度
            Float videoSeconds = FFmpegUtils.getVideoSeconds(outFilePath);
            log.info("mp3时间长度：{}", mp3TimeLength);
            if (videoSeconds != null && Math.abs(videoSeconds - mp3TimeLength) <= count) {
                // 相等
                // 删除对应的文件
                ImageCallable.delLrcFileImagePath(lrcPath);
                return;
            }
        }
        LyricsFileReader lyricsFileReader = LyricsIOUtils.getLyricsFileReader(lrcPath);
        LyricsInfo lyricsInfo = lyricsFileReader.readFile(lrcPath, mp3TimeLength);
        LyricsFileReader.againInitOffSet(lyricsInfo);
        TreeMap<Integer, LyricsLineInfo> lyricsLineTreeMap = lyricsInfo.getLyricsLineInfoTreeMap();
        log.info("mp3歌第一句时间偏移量：{}", lyricsInfo.getOffset());
        long startTime = System.currentTimeMillis();
        printImage(lrcPath, viewWidth, viewHeight, paddingHeight, fontSize, lyricsLineTreeMap, new LyricsUtils(),
                (int) lyricsInfo.getOffset());
        long endTime = System.currentTimeMillis();
        log.info("歌词生成图片完成->" + (endTime - startTime));
        boolean hasError = createImageVideo(lrcPath, lyricsInfo);
        if (!hasError) {
            // 删除对应的文件
            // 判断mp3和mp4时间的长度
            Float videoSeconds = FFmpegUtils.getVideoSeconds(outFilePath);
            if (videoSeconds != null && Math.abs(videoSeconds - mp3TimeLength) <= count) {
                // 相等
                // 删除对应的文件
                ImageCallable.delLrcFileImagePath(lrcPath);
            }
        }
    }

    /**
     * 处理当个文件
     *
     * @param viewWidth     图片宽度
     * @param viewHeight    图片高度
     * @param paddingHeight 歌词间隔
     * @param fontSize      歌词文件小打
     * @param lrcPath       lrc文件路径
     * @throws Exception
     */
    public static void runSingFile(int viewWidth, int viewHeight, int paddingHeight, int fontSize, String lrcPath) throws Exception {
        runSingFile(viewWidth, viewHeight, paddingHeight, fontSize, new File(lrcPath));
    }

    /**
     * 处理整个文件夹
     *
     * @param viewWidth     图片宽度
     * @param viewHeight    图片高度
     * @param paddingHeight 歌词间隔
     * @param fontSize      歌词文件小打
     * @param dirPath       lrc文件路径
     * @throws Exception
     */
    public static void runDirFile(int viewWidth, int viewHeight, int paddingHeight, int fontSize, String dirPath) throws Exception {
        runDirFile(viewWidth, viewHeight, paddingHeight, fontSize, new File(dirPath));
    }

    /**
     * 处理整个文件夹
     *
     * @param viewWidth     图片宽度
     * @param viewHeight    图片高度
     * @param paddingHeight 歌词间隔
     * @param fontSize      歌词文件小打
     * @param dirPath       lrc文件路径
     * @throws Exception
     */
    public static void runDirFile(int viewWidth, int viewHeight, int paddingHeight, int fontSize, File dirPath) throws Exception {
        Iterator<File> fileIterator = FileUtilsZX.iterateFiles(dirPath, new String[]{"lrc"}, true);
        while (fileIterator.hasNext()) {
            File next = fileIterator.next();
            runSingFile(viewWidth, viewHeight, paddingHeight, fontSize, next);
        }

    }

    private static boolean createImageVideo(File lrcFile, LyricsInfo lyricsInfo) throws IOException, InterruptedException {
        long startTime = System.currentTimeMillis();
        boolean hasError = convertPicToAvi(lrcFile, 10, lyricsInfo);
        long endTime = System.currentTimeMillis();
        if (hasError) {
            return true;
        } else {
            log.info("图片生成视频完成->" + (endTime - startTime));
            return false;
        }
    }

    /**
     * 将图片转换成视频
     *
     * @param lrcFile    歌词文件
     * @param fps        每秒帧数
     * @param lyricsInfo 歌词信息
     * @throws Exception
     */
    private static boolean convertPicToAvi(File lrcFile, int fps, LyricsInfo lyricsInfo) throws IOException, InterruptedException {
        fps = fps > 0 ? fps : 3;
        // jpgs目录放置jpg图片,图片文件名为(1.jpg,2.jpg...)
        String lrcFileImagePath = ImageCallable.getLrcFileImagePath(lrcFile);
        File[] files = new File(lrcFileImagePath).listFiles();
        if (files == null || files.length == 0) {
            return false;
        }
        // 生成视频
        String listFile = PathUtils.pathResolve(lrcFileImagePath, "6.txt");
        FileWriter fileWriter = new FileWriter(listFile, false);
        AtomicInteger index = new AtomicInteger(0);
        int finalFps = fps;
        Arrays.stream(files)
                .map(i -> i.getAbsolutePath().replaceAll("\\\\", "/"))
                .filter(name -> name.endsWith(".png"))
                .forEach(i -> {
                    try {
                        // 文件名中有单引号，需要将 ' 转义替换为 '\''
                        i = i.replaceAll("'", "'\\\\''");
                        // file '/path/to/abc.png'
                        fileWriter
                                .append("file '")
                                .append(i)
                                .append("'\n");
                        fileWriter.flush();
                        if (index.get() == 0) { // 处理偏移时间
                            long l = lyricsInfo.getOffset() / 10 / finalFps;
                            for (int j = 0; j < l; j++) {
                                fileWriter
                                        .append("file '")
                                        .append(i)
                                        .append("'\n");
                                fileWriter.flush();
                            }
                        }
                        index.getAndIncrement();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                });
        fileWriter.close();
        String audioFilePath = ImageCallable.getAudioFilePath(lrcFile);
        String outFile = ImageCallable.getOutFilePath(lrcFile);
        Process process = FFmpegUtils.txtImgWithAudio2Mp4(fps,
                listFile,
                audioFilePath,
                outFile);
        BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        boolean hasError = false;
        List<String> logShow50 = new LinkedList<>();
        while ((line = br.readLine()) != null) {
            logShow50.add(line);
            if (logShow50.size() > 50) {
                logShow50.remove(0);
            }
            if (line.startsWith("Error")) {
                hasError = true;
            }
        }
        if (hasError) {
            for (String line1 : logShow50) {
                log.info("子进程信息 = " + line1);
            }
        }
        process.waitFor();
        process.destroy();
        br.close();
        return hasError;
    }


    /**
     * 生成照片
     *
     * @param lrcFile           文件
     * @param width             宽度
     * @param height            高度
     * @param paddingHeight     间隔
     * @param fontSize          字体大小
     * @param lyricsLineTreeMap 歌词集合
     * @param lyricsUtils
     */
    private static void printImage(File lrcFile, int width, int height, int paddingHeight, int fontSize,
                                   TreeMap<Integer, LyricsLineInfo> lyricsLineTreeMap, LyricsUtils lyricsUtils, int offsetTime) throws ExecutionException, InterruptedException {
        int maxProgress = 0;
        if (lyricsLineTreeMap.size() > 0) {
            for (Integer integer : lyricsLineTreeMap.keySet()) {
                LyricsLineInfo lyricsLineInfo = lyricsLineTreeMap.get(integer);
                int startTime = lyricsLineInfo.getStartTime();
                int endTime = lyricsLineInfo.getEndTime();
                maxProgress = NumberUtil.max(startTime, endTime, maxProgress);
            }
        } else {
            return;
        }
        // 统一歌词字体
        String fontFilePath = Constants.PATH_FONTS + File.separator + "Arial-Unicode-Bold.ttf";
        Font kscFont = FontsUtil.getFontByFile(fontFilePath, Font.BOLD, fontSize);
        //
        List<Future<Integer>> taskList = new LinkedList<>();
        String lrcFileImagePath = ImageCallable.getLrcFileImagePath(lrcFile) + File.separator;
        List<String> imgsPath = new LinkedList<>();
        while (offsetTime < maxProgress + 100) {
            String imgPath = lrcFileImagePath + ImageCallable.addZeroForNum(offsetTime + "", ((maxProgress + 100) + "").length()) + ".png";
            imgsPath.add(PathUtils.pathStandardization(imgPath));
            // 先判断图片是否存在，不存在就执行
            if (!FileUtilsZX.exists(new File(imgPath))) {
                ImageCallable imageCallable = new ImageCallable(
                        lrcFile,
                        width,
                        height,
                        kscFont,
                        lyricsUtils,
                        lyricsLineTreeMap,
                        offsetTime,
                        0,
                        paddingHeight,
                        maxProgress);
                taskList.add(executorService.submit(imageCallable));
            }
            offsetTime += 100;
        }
        for (Future<Integer> integerFutureTask : taskList) {
            // 阻塞主线程
            integerFutureTask.get();
        }
        // 清除多余的png文件
        File[] files = new File(lrcFileImagePath).listFiles();
        if (files == null || files.length == 0) {
            return;
        }
        Arrays.stream(files)
                .map(i -> i.getAbsolutePath().replaceAll("\\\\", "/"))
                .filter(name -> name.endsWith(".png"))
                .map(PathUtils::pathStandardization)
                .filter(i -> !imgsPath.contains(i))
                .forEach(FileUtilsZX::deleteFile);
    }
}
